#include "Node2D.h"
#include <math.h>

Node2D::Node2D(void)
:mPos(CCPointZero)
,mAnchorPoint(CCPointZero)
,mContentSize(CCSizeZero)
,mAnchorPointInPoints(CCPointZero)
,mRotation(0)
,mScaleX(1)
,mScaleY(1)
,mSkewX(0)
,mSkewY(0)
,mAlpha(255)
{
}


Node2D::~Node2D(void)
{
}
void Node2D::update(unsigned int stageWidth,unsigned int stageHeight)
{
	//0 1
	//3 2
	float alpha = nodeToWorldAlpha();
	Color4B color = {255,255,255,alpha};
	float pos_z = 0;
	float left = 0;
	float right = 0 + mContentSize.width;
	float top = 0 + mContentSize.height;
	float bottom = 0;

	mQuad.tl.vertices = vertex3(left, top, pos_z);
	mQuad.tl.texCoords = tex2(0.0f,0.0f);
	mQuad.tl.colors = color;

	mQuad.tr.vertices = vertex3(right, top, pos_z);
	mQuad.tr.texCoords = tex2(1.0f,  0.0f);
	mQuad.tr.colors = color;

	mQuad.br.vertices = vertex3(right,bottom, pos_z);
	mQuad.br.texCoords = tex2(1.0f,  1.0f);
	mQuad.br.colors = color;

	mQuad.bl.vertices = vertex3(left, bottom, pos_z);
	mQuad.bl.texCoords = tex2(0.0f,  1.0f);
	mQuad.bl.colors = color;

	CCAffineTransform trans = nodeToWorldTransform();
	doTransfrom(mQuad.tl.vertices,trans);
	doTransfrom(mQuad.tr.vertices,trans);
	doTransfrom(mQuad.bl.vertices,trans);
	doTransfrom(mQuad.br.vertices,trans);
}
void Node2D::draw(Technique* pEffect)
{
	V3F_C4B_T2F_Quad* quad = &mQuad;
	long offset = (long)quad;
	// vertex
	int diff = offsetof( V3F_C4B_T2F, vertices);
	glVertexAttribPointer(pEffect->getAttribLocation("a_position"), 3, GL_FLOAT, GL_FALSE, sizeof(quad->bl), (void*) (offset + diff));

	// texCoods
	diff = offsetof( V3F_C4B_T2F, texCoords);
	glVertexAttribPointer(pEffect->getAttribLocation("a_texCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(quad->bl), (void*)(offset + diff));

	// color
	diff = offsetof( V3F_C4B_T2F, colors);
	glVertexAttribPointer(pEffect->getAttribLocation("a_color"), 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(quad->bl), (void*)(offset + diff));

	glEnableVertexAttribArray ( pEffect->getAttribLocation("a_position") );
	glEnableVertexAttribArray ( pEffect->getAttribLocation("a_texCoord") );
	glEnableVertexAttribArray ( pEffect->getAttribLocation("a_color") );

	if (pTexture!= NULL)
	{
		pTexture->Bind(GL_TEXTURE0);
	}
	// Set the base map sampler to texture unit to 0
	glUniform1i ( pEffect->getUniformLocation("s_baseMap"), 0 );
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
void Node2D::setTexture(Texture* texture)
{
	pTexture = texture;
}
void Node2D::setName(std::string name)
{
	//convert const char* to char*
	mName = new char[name.size() + 1];
	std::strcpy ( mName, name.c_str() );
	//mName = const_cast<char*> ( name.c_str() );
}
std::string Node2D::getName()
{
	return mName;
}
void Node2D::doTransfrom(Vertex3F& vertices,CCAffineTransform transform)
{
	CCPoint point(vertices.x,vertices.y);
	point = CCPointApplyAffineTransform(point,transform);
	vertices.x = point.x;
	vertices.y = point.y;
}
void Node2D::addChild(Node2D* child)
{
	if (isIncludeChild(child))
	{
		return;
	}
	child->setParent(this);
	mChildren.push_back(child);
}
void Node2D::addChild(Node2D* child,int index)
{
	if (isIncludeChild(child))
	{
		return;
	}
	if (index >= count())
	{
		addChild(child);
		return;
	}
	if (index < 0)
	{
		index = 0;
	}
	mChildren.insert(mChildren.begin()+index,child);
}
Node2D* Node2D::getChildAt(int index)
{
	if (index < 0 || index >= count())
	{
		return NULL;
	}
	return mChildren[index];
}
int Node2D::getChildIndex(Node2D* child)
{
	if (child == NULL)
	{
		return -1;
	}
	std::vector<Node2D*>::iterator it;
	int index = 0;
	for (it=mChildren.begin(); it<mChildren.end(); it++ ,index++)
	{
		if ((*it) == child)
		{
			return index;
		}
	}
	return -1;
}
void Node2D::removeChild(Node2D* child)
{
	if (!isIncludeChild(child))
	{
		return;
	}
	std::vector<Node2D*>::iterator it;
	for (it=mChildren.begin(); it<mChildren.end(); it++)
	{
		if ((*it) == child)
		{
			child->setParent(NULL);
			mChildren.erase(it);
			break;
		}
	}
}
void Node2D::removeChildAt(int index)
{
	if (index < 0 || index >= count())
	{
		return;
	}
	std::vector<Node2D*>::iterator it;
	int i = 0;
	for (it=mChildren.begin(); it<mChildren.end(); it++ ,i++)
	{
		if (i == index)
		{
			(*it)->setParent(NULL);
			mChildren.erase(it);
			break;
		}
	}
}
void Node2D::removeAllChildren()
{
	std::vector<Node2D*>::iterator it;
	for (it=mChildren.end(); it<mChildren.begin(); it--)
	{
		(*it)->setParent(NULL);
		(*it)->removeAllChildren();
		mChildren.erase(it);
	}
}
void Node2D::setChildIndex(Node2D* child,int index)
{
	if (!isIncludeChild(child))
	{
		return;
	}
	removeChild(child);
	addChild(child,index);
}
void Node2D::swapChildren(Node2D* child1,Node2D* child2)
{
	if (!isIncludeChild(child1) || !isIncludeChild(child2))
	{
		return;
	}
	int index1 = getChildIndex(child1);
	int index2 = getChildIndex(child2);
	if (index1 > -1 && index2 > -1)
	{
		mChildren[index1] = child2;
		mChildren[index2] = child1;
	}
}
void Node2D::swapChildrenAt(int index1,int index2)
{
	swapChildren(getChildAt(index1),getChildAt(index2));
}
int Node2D::count()
{
	return mChildren.size();
}
void Node2D::setParent(Node2D* parent)
{
	mParent = parent;
}
Node2D* Node2D::getParent()
{
	return mParent;
}
bool Node2D::isIncludeChild(Node2D* child)
{
	if (child == NULL || child->getParent() == NULL || child->getParent()!= this)
	{
		return false;
	}
	return true;
}
CCAffineTransform Node2D::nodeToParentTransform()
{
	float x = mPos.x;
	float y = mPos.y;
	float cr = 1;
	float sr = 0;
	CCPoint anchorPoint(mAnchorPoint.x * mContentSize.width,mAnchorPoint.y * mContentSize.height);
	if (mRotation)
	{
		cr = cosf(mRotation);
		sr = sinf(mRotation);
	}
	bool needsSkewMatrix = ( mSkewX || mSkewY );

	if (! needsSkewMatrix && !anchorPoint.equals(CCPointZero))
	{
		x += cr * -anchorPoint.x * mScaleX + -sr * -anchorPoint.y * mScaleY;
		y += sr * -anchorPoint.x * mScaleX +  cr * -anchorPoint.y * mScaleY;
	}


	CCAffineTransform  trans  = CCAffineTransformMake( cr * mScaleX,  sr * mScaleX,
		-sr * mScaleY, cr * mScaleY,
		x, y );

	if (needsSkewMatrix) 
	{
		CCAffineTransform skewTransform = CCAffineTransformMake(1.0f,tan(mSkewY),tan(mSkewX),1.0f,0.0f,0.0f);
		trans = CCAffineTransformConcat(skewTransform, trans);

		if (!anchorPoint.equals(CCPointZero))
		{
			trans = CCAffineTransformTranslate(trans, -anchorPoint.x, -anchorPoint.y);
		}
	}
	/*CCAffineTransform rotationTransform = CCAffineTransformMake(cosf(mRotation),sinf(mRotation),-sinf(mRotation),cosf(mRotation),0,0);
	CCAffineTransform position_scale_transform = CCAffineTransformMake(mScaleX,0,0,mScaleY,mPos.x,mPos.y);
	CCAffineTransform  trans = CCAffineTransformConcat(rotationTransform,position_scale_transform);
	bool needsSkewMatrix = ( mSkewX || mSkewY );
	if (needsSkewMatrix)
	{
	CCAffineTransform skewTransform = CCAffineTransformMake(1.0f,tan(mSkewY),tan(mSkewX),1.0f,0.0f,0.0f);
	trans = CCAffineTransformConcat(skewTransform,trans);
	}*/
	return trans;
}
CCAffineTransform Node2D::parentToNodeTransform()
{
	return CCAffineTransformInvert(this->nodeToParentTransform());
}
CCAffineTransform Node2D::nodeToWorldTransform()
{
	CCAffineTransform t = this->nodeToParentTransform();
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		t = CCAffineTransformConcat(t, p->nodeToParentTransform());
	return t;
}
CCAffineTransform Node2D::worldToNodeTransform()
{
	 return CCAffineTransformInvert(this->nodeToWorldTransform());
}
CCPoint Node2D::convertToNodeSpace(const CCPoint& worldPoint)
{
	CCPoint ret = CCPointApplyAffineTransform(worldPoint, worldToNodeTransform());
	return ret;
}
CCPoint Node2D::convertToWorldSpace(const CCPoint& nodePoint)
{
	CCPoint ret = CCPointApplyAffineTransform(nodePoint, nodeToWorldTransform());
	return ret;
}
float Node2D::nodeToWorldAlpha()
{
	float a = mAlpha/255;
	for (Node2D *p = mParent; p != NULL; p = p->getParent())
		a*= p->mAlpha/255;
	return a*255;
}

